#include <kew/c-vm.h>

/*
 * Object description and creation functions.
 */

KewObjectType *kew_type_register(KewStaticEnvironment *static_environment, KewSourcePoint source_point)
{
    KewObjectType *type;
    unsigned slot;

    type = kew_new_padded(KewObjectType, KewDispatchMap, KEW_MAX_SELECTORS);
    
    for (slot=0; slot<KEW_MAX_SELECTORS; ++slot) {
        type->dispatch_map[slot].callee_shape = KEW_CALL_SHAPE_DELEGATE;
        type->dispatch_map[slot].method = kew_not_understood_method;
        type->dispatch_map[slot].source_point = "(undefined method)";
    }

    type->static_environment = static_environment;
    type->source_point = source_point;
    type->method_map = g_hash_table_new(g_direct_hash, g_direct_equal);
    type->extensions = g_hash_table_new(g_direct_hash, g_direct_equal);

    return type;
}


KewSelector kew_selector_register(KewVM *vm, char *selector_name)
{
    char *original_name;
    KewSelector slot;

    gboolean found = g_hash_table_lookup_extended(vm->selectors, selector_name, &original_name, &slot);
    
    if (!found) {
        slot = g_hash_table_size(vm->selectors);
        g_hash_table_insert(vm->selectors, selector_name, (gpointer) slot);
        return slot;
    }
    return slot;
}


void kew_method_register(KewObjectType *type, KewSelector selector, KewMethod method,
                         KewCallShape callee_shape, KewSourcePoint source_point)
{
    KewDispatchMap *map;

    map = &(type->dispatch_map[selector]);
    map->callee_shape = callee_shape;
    map->method = method;
    map->source_point = source_point;

    g_hash_table_insert(type->method_map, (gpointer) selector, (gpointer) map);
}


/* Check the type of an object, coercing it to the correct type if possible
 * by sending a type coercion message.  If it is not possible, return an error object.
 * To behave sanely, the caller must check the error value to see if an error
 * has been returned.
 */
KewObject *kew_cast(KewVM *vm, KewObject *object, KewObjectType *required_type,
                    char *required_type_name, KewSelector coercion_method,
                    KewSourcePoint source_point, gboolean *error)
{
    *error = FALSE;
    
    if (kew_type_check(required_type, object->type == required_type)
        return object;
    if (kew_method_understood(coercion_method, object))
        return kew_send_01(object, coercion_method, source_point);
    
    error = TRUE;
    g_error("FIXME: should create an error object here?");
    return NULL;
}

KewObject *kew_object_alloc(KewObjectType *type)
{
    return (KewObject *) kew_alloc(type->object_size_in_bytes);
}

void kew_object_init(KewObject *object)
{
    object->type = type;
    object->extensions = NULL;
}

KewObject *kew_object_new(KewObjectType *type)
{
    KewObject *object = kew_object_alloc(type);
    kew_object_init(object);
    return object;
}

void kew_user_object_init(KewUserObject *object)
{
    kew_user_object_init(object);
    /* FIXME: could set up capture slots here with safe initial value */
}

KewUserObject *kew_user_object_new(KewObjectType *type)
{
    KewUserObject *object = (KewUserObject *) kew_object_alloc(type);
    kew_object_init(object);
    return object;
}

KewUserObject *kew_user_object_new_begin(KewObjectType *type)
{
    KewUserObject *object = (KewUserObject *) kew_object_alloc(type);
    kew_user_object_init(object);
    kew_object_become_incomplete(object);
    return object;
}

void kew_user_object_new_end(KewObject *object, KewObjectType *type)
{
    kew_object_become_complete(object, type);
}
